package com.xiaoyu.base.utils.upload

import `in`.srain.cube.request.*
import `in`.srain.cube.util.CLog
import `in`.srain.cube.util.CheckUtil
import `in`.srain.cube.util.StringUtil
import `in`.srain.cube.util.general.ObjectUtil
import `in`.srain.cube.util.internal.CodeException
import `in`.srain.cube.util.log.LogEvent
import android.os.SystemClock
import com.qiniu.android.http.ResponseInfo
import com.qiniu.android.storage.*
import com.xiaoyu.base.config.HttpConfig
import com.xiaoyu.base.log.AppPerformanceLogClient
import com.xiaoyu.net.request.RequestWithJsonDataReturn
import org.json.JSONObject
import java.util.concurrent.CountDownLatch
import java.util.concurrent.atomic.AtomicReference

class QiniuUploadManager {

    private var mUploadManager: UploadManager? = null

    fun init(config: Configuration?) {
        mUploadManager = UploadManager(config)
    }

    @Throws(java.lang.Exception::class)
    fun uploadSync(uploadParam: UploadParam): String {
        val successUrlReference = AtomicReference<String>()
        val exceptionReference = AtomicReference<java.lang.Exception?>()
        val latch = CountDownLatch(1)
        upload(uploadParam, object : UploadListener() {
            override fun onFailure(exception: Exception) {
                exceptionReference.set(exception)
                latch.countDown()
            }

            override fun onSuccess(url: String) {
                successUrlReference.set(url)
                latch.countDown()
            }
        })
        latch.await()
        exceptionReference.get()?.let {
            throw it
        }
        return CheckUtil.checkNotNull(successUrlReference.get(), "success is null")
    }

    fun upload(uploadParam: UploadParam, listener: UploadListener) {
        getUploadTokenFromRemote(uploadParam, object : RequestFinishHandler<QiniuUploadInfo> {
            override fun onRequestFinish(uploadInfo: QiniuUploadInfo) {
                uploadToQiniu(uploadParam, uploadInfo, listener)
            }

            override fun onRequestFail(failData: FailData) {
                val exception: Exception = ObjectUtil.defaultIfNull(failData.exception, { Exception() })
                listener.onFailure(exception)
            }
        })
    }

    private fun uploadToQiniu(uploadParam: UploadParam, uploadInfo: QiniuUploadInfo, listener: UploadListener) {
        val progressHandler = UpProgressHandler { key: String?, percent: Double -> listener.onUpload(percent) }
        val start = SystemClock.elapsedRealtime()
        val handler = UpCompletionHandler { key: String, info: ResponseInfo, res: JSONObject? ->
            val manualDuration = SystemClock.elapsedRealtime() - start
            val url = "${uploadInfo.urlPre}/${key}"
            logUploadResult(uploadParam, url, info, manualDuration)
            if (info.isOK) {
                listener.onSuccess(url)
            } else {
                listener.onFailure(CodeException(info.error, info.statusCode))
            }
        }
        val options = UploadOptions(null, null, false, progressHandler, null)
        mUploadManager?.put(uploadParam.filePath, uploadInfo.path, uploadInfo.token, handler, options)
    }

    private class QiniuUploadInfo internal constructor(val path: String, val urlPre: String, val token: String) {
        override fun toString(): String {
            return "QiniuUploadInfo{" +
                    "path='" + path + '\'' +
                    ", urlPre='" + urlPre + '\'' +
                    ", token='" + token + '\'' +
                    '}'
        }

        companion object {
            fun fromJsonOrNull(jsonData: JsonData): QiniuUploadInfo? {
                val pathPre: String = jsonData.optString("path")
                val urlPre: String = jsonData.optString("urlPre")
                val token: String = jsonData.optString("token")
                return if (StringUtil.isAnyEmpty(pathPre, urlPre, token)) null else QiniuUploadInfo(pathPre, urlPre, token)
            }
        }
    }

    companion object {
        private const val TAG = "QiniuUploadManager"
        val instance = QiniuUploadManager()

        private fun getUploadTokenFromRemote(uploadParam: UploadParam, finishHandler: RequestFinishHandler<QiniuUploadInfo>) {
            val request = RequestWithJsonDataReturn<QiniuUploadInfo>()
            val requestData: RequestData = request.requestData
            requestData.requestUrl = HttpConfig.APP_QINIU_TOKEN
            uploadParam.addToRequestData(requestData)
            request.setRequestHandler(finishHandler, object : RequestDefaultHandler<QiniuUploadInfo, JsonData>() {
                override fun processOriginData(originData: JsonData): QiniuUploadInfo? {
                    return QiniuUploadInfo.fromJsonOrNull(originData.optJson("data"))
                            ?: throw IllegalStateException("create QiniuUploadInfo failed")
                }
            })
            request.enqueue()
        }

        private fun logUploadResult(uploadParam: UploadParam, url: String, info: ResponseInfo, manualDuration: Long) {
            val logEvent = LogEvent("uploadResult")
            logEvent.putData("uploadWay", "qiniu")
            logEvent.putData("url", url)
            logEvent.putData("category", uploadParam.category)
            logEvent.putData("mediaType", uploadParam.mediaType)
            logEvent.putData("chatId", uploadParam.chatId)
            logEvent.putData("success", info.isOK)
            logEvent.putData("error", info.error)
            logEvent.putData("code", info.statusCode)
            logEvent.putData("size", info.totalSize)
            logEvent.putData("duration", info.duration)
            logEvent.putData("manualDuration", manualDuration)
            AppPerformanceLogClient.instance.addLog(logEvent)
        }
    }
}